import { pickedLineData } from '@amcax/base';
import { Picker3D, PickerMgr } from '@amcax/renderer';
import {
  MultiPickInput,
  PickInputValueType,
} from '@app-cad/common/components/pick-input/3d';
import { useFormLocalState } from '@app-cad/common/hooks/useFormLocalState';
import { userEvents } from '@app-cad/module';
import { WidgetProps } from '@rjsf/utils';
import { Button, Form, Input, Modal, Row, Select } from 'antd';
import 'echarts/lib/chart/line';
import * as echarts from 'echarts/lib/echarts.js';
import { isEqual } from 'lodash';
import {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import type { DraggableData, DraggableEvent } from 'react-draggable';
import Draggable from 'react-draggable';
import { LawCurve } from '../types';
import { getLawCurveFromJSONString } from '../utils';
import './style.scss';

const LawCurveModalOpenedEvent = 'LAW-CURVE-MODAL-OPENED';

const selectOption = [
  {
    value: 'Constant',
    label: '常量',
  },
  {
    value: 'Linear',
    label: '线性',
  },
  {
    value: 'S',
    label: 'S型',
  },
  {
    value: 'BSpline',
    label: '高级',
  },
];

const LawCurveModal = ({
  form,
  mainValue,
  updateMainValue,
  open,
  onOk,
  setFormData,
  suffix,
  lawCurveValueStep,
  lawCurveValueMin,
  lawCurveValueMax,
}: {
  form: LawCurve;
  mainValue: number;
  updateMainValue: (value: number) => void;
  open: boolean;
  onOk: any;
  setFormData: any;
  suffix: string;
  lawCurveValueStep: number;
  lawCurveValueMin?: number;
  lawCurveValueMax?: number;
}) => {
  const [disabled, setDisabled] = useState(true);
  const [bounds, setBounds] = useState({
    left: 0,
    top: 0,
    bottom: 0,
    right: 0,
  });
  const draggleRef = useRef<HTMLDivElement>(null);
  const chartsNode = useRef(null);

  const [localForm, setLocalForm] = useFormLocalState<LawCurve>(form);
  const { type = 'Constant', start = 20, end = 0, bsplineId } = localForm;

  useEffect(() => {
    if (type === 'Constant' || type === 'Linear' || type === 'S') {
      setLocalForm({
        ...localForm,
        start: mainValue,
      });
    }
  }, [type, mainValue]);

  const RowMarginTop = '8px';

  const xLineOption = {
    data: [0, 0.5, 1],
    boundaryGap: false,
    axisTick: {
      length: 0.5,
      lineStyle: {
        type: 'dashed',
      },
    },
    splitLine: {
      show: true, // 是否显示分隔线。默认数值轴显示，类目轴不显示
      lineStyle: {
        type: 'dashed', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    axisLine: {
      show: true,
      onZero: false,
      symbol: ['none', 'arrow'], // 轴线两端箭头，两个值，none表示没有箭头，arrow表示有箭头
      symbolSize: [5, 10], // 轴线两端箭头大小，数值一表示宽度，数值二表示高度
      symbolOffset: [35],
      lineStyle: {
        color: '#333', // 坐标轴线线的颜色
        shadowOffsetX: 35,
        width: '1', // 坐标轴线线宽
        type: 'solid', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    name: '参数',
    nameLocation: 'middle',
    nameGap: 30,
  };

  const yLineOption = {
    splitNumber: 2,
    max: type === 'Constant' ? Math.max(start, 0) : Math.max(start, end),
    min: type === 'Constant' ? Math.min(start, 0) : Math.min(start, end),
    name: `振幅(${suffix})`,
    nameLocation: 'middle',
    nameGap: 30,
    axisTick: {
      lineStyle: {
        type: 'dashed',
      },
    },
    splitLine: {
      show: true, // 是否显示分隔线。默认数值轴显示，类目轴不显示
      lineStyle: {
        // color: ['#979797'], //color分隔线颜色，可设置单个颜色，也可设置颜色数组，分隔线会按数组中颜色顺序依次循环设置颜色
        type: 'dashed', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    axisLine: {
      show: true,
      onZero: false,
      symbol: ['none', 'arrow'], // 轴线两端箭头，两个值，none表示没有箭头，arrow表示有箭头
      symbolSize: [5, 10], // 轴线两端箭头大小，数值一表示宽度，数值二表示高度
      symbolOffset: [35],
      lineStyle: {
        color: '#333', // 坐标轴线线的颜色
        shadowOffsetY: -30,
        width: '1', // 坐标轴线线宽
        type: 'solid', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
  };

  const xSOption = {
    data: [0, 0.25, 0.5, 0.75, 1],
    boundaryGap: false,
    axisLabel: {
      interval: function (index) {
        return index % 2 === 0;
      },
    },
    axisTick: {
      lineStyle: {
        type: 'dashed',
      },
    },
    splitLine: {
      show: true, // 是否显示分隔线。默认数值轴显示，类目轴不显示
      lineStyle: {
        type: 'dashed', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    axisLine: {
      show: true,
      onZero: false,
      symbol: ['none', 'arrow'], // 轴线两端箭头，两个值，none表示没有箭头，arrow表示有箭头
      symbolSize: [5, 10], // 轴线两端箭头大小，数值一表示宽度，数值二表示高度
      symbolOffset: [35],
      lineStyle: {
        color: '#333', // 坐标轴线线的颜色
        shadowOffsetX: 35,
        width: '1', // 坐标轴线线宽
        type: 'solid', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    name: '参数',
    nameLocation: 'middle',
    nameGap: 30,
  };

  const ySOption = {
    splitNumber: 2,
    min: type === 'S' ? Math.min(start, end) : 0,
    max: type === 'S' ? Math.max(start, end) : 100,
    name: `振幅(${suffix})`,
    nameLocation: 'middle',
    nameGap: 30,
    axisTick: {
      lineStyle: {
        type: 'dashed',
      },
    },
    splitLine: {
      show: true, // 是否显示分隔线。默认数值轴显示，类目轴不显示
      lineStyle: {
        // color: ['#979797'], //color分隔线颜色，可设置单个颜色，也可设置颜色数组，分隔线会按数组中颜色顺序依次循环设置颜色
        type: 'dashed', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
    axisLine: {
      show: true,
      onZero: false,
      symbol: ['none', 'arrow'], // 轴线两端箭头，两个值，none表示没有箭头，arrow表示有箭头
      symbolSize: [5, 10], // 轴线两端箭头大小，数值一表示宽度，数值二表示高度
      symbolOffset: [35],
      lineStyle: {
        color: '#333', // 坐标轴线线的颜色
        shadowOffsetY: -30,
        width: '1', // 坐标轴线线宽
        type: 'solid', // 坐标轴线线的类型（solid实线类型；dashed虚线类型；dotted点状类型）
      },
    },
  };

  const updateFormAndChart = () => {
    setFormData(localForm);

    const mid = (start + end) / 2;
    const leftP =
      start === end
        ? mid
        : start > end
          ? ((mid + start) / 2) * 0.87
          : ((mid + start) / 2) * 1.13;
    const rightP =
      start === end
        ? mid
        : start > end
          ? ((mid + end) / 2) * 1.13
          : ((mid + end) / 2) * 0.87;
    if (chartsNode.current && echarts.getInstanceByDom(chartsNode.current)) {
      echarts.dispose(chartsNode.current);
    }
    const myCharts = chartsNode.current && echarts.init(chartsNode.current);
    let option = {};
    if (type === 'Constant') {
      option = {
        xAxis: xLineOption,
        yAxis: yLineOption,
        grid: {
          show: true,
          left: '15%',
          right: '15%',
        },
        series: [
          {
            data: [start, start, start],
            type: 'line',
            lineStyle: {
              color: '#E53F38',
              width: 2,
            },
            symbol: 'none',
          },
        ],
      };
    }

    if (type === 'Linear') {
      option = {
        xAxis: xLineOption,
        yAxis: yLineOption,
        grid: {
          show: true,
          left: '15%',
          right: '15%',
        },
        series: [
          {
            data: [start, mid, end],
            type: 'line',
            lineStyle: {
              color: '#E53F38',
              width: 2,
            },
            symbol: 'none',
          },
        ],
      };
    }

    if (type === 'S' || type === 'BSpline') {
      option = {
        xAxis: xSOption,
        yAxis: ySOption,
        grid: {
          show: true,
          left: '15%',
          right: '15%',
        },
        series: [
          {
            data: [start, leftP, mid, rightP, end],
            smooth: true,
            type: 'line',
            lineStyle: {
              color: '#E53F38',
              width: 2,
            },
            symbol: 'none',
          },
        ],
      };
    }

    option && myCharts && myCharts.setOption(option);
  };

  useEffect(() => {
    if (open) {
      setTimeout(() => {
        updateFormAndChart();
      }, 0);
    }
  }, [open, form.type, form.start, form.end, form.bsplineId]);

  const handleTypeChange = async (value: string) => {
    setFormData({
      ...localForm,
      type: value,
      start: 0,
      end: 0,
    });
  };

  const [formInstance] = Form.useForm();

  useLayoutEffect(() => {
    formInstance.setFieldsValue({
      start,
      end,
      type,
    });

    if (type === 'Constant') {
      updateMainValue(start);
    } else if (type === 'Linear' || type === 'S') {
      updateMainValue(start);
    }
  }, [localForm, formInstance]);

  const onFieldsChange = () => {
    const values = formInstance.getFieldsValue();
    setLocalForm({
      ...localForm,
      ...values,
    });
  };

  const onStart = (_event: DraggableEvent, uiData: DraggableData) => {
    const { clientWidth, clientHeight } = window.document.documentElement;
    const targetRect = draggleRef.current?.getBoundingClientRect();
    if (!targetRect) {
      return;
    }
    setBounds({
      left: -targetRect.left + uiData.x,
      right: clientWidth - (targetRect.right - uiData.x),
      top: -targetRect.top + uiData.y,
      bottom: clientHeight - (targetRect.bottom - uiData.y),
    });
  };

  const onClose = () => {
    onOk();
  };

  const picker = PickerMgr.getPicker('3d') as Picker3D;

  return (
    <Modal
      mask={false}
      style={{ pointerEvents: 'none' }}
      wrapClassName='pointer-events-none'
      getContainer='#app-cad_root'
      open={open}
      maskClosable={false}
      okText='关闭'
      width={376}
      onOk={onClose}
      onCancel={onClose}
      cancelButtonProps={{ style: { display: 'none' } }}
      title={
        <div
          style={{ width: '100%', cursor: 'move' }}
          onMouseOver={() => {
            if (disabled) {
              setDisabled(false);
            }
          }}
          onMouseOut={() => {
            setDisabled(true);
          }}
          onFocus={() => {}}
          onBlur={() => {}}
        >
          法则曲线
        </div>
      }
      modalRender={(modal) => (
        <Draggable
          disabled={disabled}
          bounds={bounds}
          nodeRef={draggleRef}
          onStart={(event, uiData) => onStart(event, uiData)}
        >
          <div ref={draggleRef}>{modal}</div>
        </Draggable>
      )}
    >
      <div className='law-curve-modal-style'>
        <div
          ref={chartsNode}
          style={{ width: 328, height: 224, backgroundColor: '#D6D8DB' }}
        />
        <Form
          form={formInstance}
          labelCol={{ span: 8 }}
          wrapperCol={{ offset: 4, span: 8 }}
          onFieldsChange={onFieldsChange}
          initialValues={{
            start,
            end,
            type,
          }}
        >
          <Form.Item
            label='法则曲线类型'
            name='type'
            style={{ marginTop: RowMarginTop }}
          >
            <Select
              style={{ width: 140 }}
              options={selectOption}
              onChange={handleTypeChange}
            />
          </Form.Item>
          {type === 'Constant' && (
            <Form.Item
              label='常量值'
              name='start'
              style={{ marginTop: RowMarginTop }}
              rules={[{ required: true, message: '请输入常量值' }]}
            >
              <Input
                style={{ width: '140px' }}
                type='number'
                suffix={suffix}
                onBlur={updateFormAndChart}
                step={lawCurveValueStep}
                min={lawCurveValueMin}
                max={lawCurveValueMax}
              />
            </Form.Item>
          )}
          {(type === 'Linear' || type === 'S') && (
            <>
              <Form.Item
                label='起始值'
                name='start'
                style={{ marginTop: RowMarginTop }}
                rules={[{ required: true, message: '请输入起始值' }]}
              >
                <Input
                  type='number'
                  style={{ width: '140px' }}
                  suffix={suffix}
                  onBlur={updateFormAndChart}
                  step={lawCurveValueStep}
                  min={lawCurveValueMin}
                  max={lawCurveValueMax}
                />
              </Form.Item>
              <Form.Item
                label='结束值'
                name='end'
                style={{ marginTop: RowMarginTop }}
                rules={[{ required: true, message: '请输入结束值' }]}
              >
                <Input
                  type='number'
                  style={{ width: '140px' }}
                  suffix={suffix}
                  onBlur={updateFormAndChart}
                  step={lawCurveValueStep}
                  min={lawCurveValueMin}
                  max={lawCurveValueMax}
                />
              </Form.Item>
            </>
          )}
          {type === 'BSpline' && (
            <MultiPickInput
              style={{ marginTop: RowMarginTop }}
              autoFocus
              value={[
                picker.generatePickObjByPickID(bsplineId) as pickedLineData,
              ]}
              allowPickType={[PickInputValueType.LawCurve]}
              onCurvedLawPick={(result) => {
                setFormData({
                  ...localForm,
                  bsplineId: result?.dxid,
                });
              }}
              placeholder='请输入法则曲线元素'
              onBlur={updateFormAndChart}
            />
          )}
        </Form>
      </div>
    </Modal>
  );
};

export const LawCurveButton = (props: WidgetProps) => {
  const [lawCurveDialogOpen, setLawCurveDialogOpen] = useState(false);
  const lawCurveWidgetId = useRef<symbol>();

  useEffect(() => {
    lawCurveWidgetId.current = Symbol();

    const abortController = new AbortController();
    userEvents.on(
      LawCurveModalOpenedEvent,
      (widgetId) => {
        if (widgetId !== lawCurveWidgetId.current) {
          setLawCurveDialogOpen(false);
        }
      },
      abortController.signal,
    );

    return () => abortController.abort();
  }, []);

  useEffect(() => {
    if (lawCurveDialogOpen) {
      userEvents.emit(LawCurveModalOpenedEvent, lawCurveWidgetId.current);
    }
  }, [lawCurveDialogOpen]);

  const { value, onChange, uiSchema } = props;
  const form = useMemo(() => getLawCurveFromJSONString(value), [value]);
  const setFormData = useCallback(
    (lawCurve: LawCurve) => {
      const newForm = getLawCurveFromJSONString(JSON.stringify(lawCurve));

      if (!isEqual(form, newForm)) {
        onChange(JSON.stringify(newForm));
      }
    },
    [onChange, value],
  );

  const lawCurveValueStep = uiSchema?.['ui:options']
    ?.lawCurveValueStep as number;
  const lawCurveValueMin = uiSchema?.['ui:options']?.lawCurveValueMin as number;
  const lawCurveValueMax = uiSchema?.['ui:options']?.lawCurveValueMax as number;
  const suffix = uiSchema?.['ui:options']?.suffix as string;

  const { start, type } = form;

  const [mainValue, updateMainValue] = useFormLocalState(
    type === 'Constant'
      ? start
      : type === 'Linear' || type === 'S'
        ? start
        : null,
  );

  const onValueChange = (e) => {
    if (type === 'Constant') {
      const start = Number(e.target.value);
      setFormData({ ...form, start });
    }

    if (type === 'Linear' || type === 'S') {
      const start = Number(e.target.value);
      setFormData({ ...form, start });
    }
  };

  return (
    <div>
      <Row
        justify={'space-between'}
        wrap={false}
      >
        <Input
          type='number'
          disabled={type === 'BSpline'}
          className='law-curve-number-input'
          value={mainValue}
          suffix={suffix}
          onChange={(e) => updateMainValue(e.target.value as any)}
          onBlur={onValueChange}
          width={100}
          step={lawCurveValueStep}
          min={lawCurveValueMin}
          max={lawCurveValueMax}
        />
        <Button
          className='law-curve-button-style'
          onClick={() => setLawCurveDialogOpen(true)}
        >
          法则曲线
        </Button>
      </Row>
      <LawCurveModal
        form={form}
        mainValue={mainValue}
        updateMainValue={updateMainValue}
        open={lawCurveDialogOpen}
        onOk={() => setLawCurveDialogOpen(false)}
        setFormData={setFormData}
        suffix={suffix}
        lawCurveValueStep={lawCurveValueStep}
        lawCurveValueMin={lawCurveValueMin}
        lawCurveValueMax={lawCurveValueMax}
      />
    </div>
  );
};
